#include<bits/stdc++.h>
using namespace std;

const int N = 30000 + 10, M = 100000 + 10;
vector<int> e[N];
vector<int> back[N]; // 存储反向图的边
int in[N];           // 存储每个点的入度
int u[M], v[M];      // 存储原始边的起点和终点
bitset<N> to[N];     // to[x] 表示从 x 出发能到达的点的集合 (不包括 x 自己，但包括直接邻居)
bitset<N> cm[N];     // cm[x] 表示能到达 x 的点的集合 (不包括 x 自己，但包括直接前驱)
int tupo[N], cnt = 0; // tupo 存储拓扑排序结果, cnt 是拓扑序列中的节点数
int n, m;

void topsort() {
    queue<int> q;
    for (int i = 1; i <= n; ++i) {
        if (in[i] == 0) {
            q.push(i);
        }
    }
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        tupo[++cnt] = x; // 将节点加入拓扑序列
        for (int y : e[x]) {
            in[y]--;
            if (in[y] == 0) {
                q.push(y);
            }
        }
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> n >> m;
    for (int i = 1; i <= m; ++i) {
        cin >> u[i] >> v[i];
        e[u[i]].push_back(v[i]);
        back[v[i]].push_back(u[i]); // 构建反向图
        in[v[i]]++;                 // 统计入度
    }

    topsort(); // 进行拓扑排序

    // 计算 to 集合：从每个点出发能到达的点集
    // 按照拓扑序的逆序处理
    for (int i = cnt; i >= 1; --i) { // 注意这里是从 cnt 开始，因为拓扑序列可能不包含所有n个点（如果图不连通，但DAG问题通常假定连通或处理连通分量）
                                      // 题目保证是DAG，所以所有点都会在拓扑序列中
        int x = tupo[i];
        to[x][x] = 1; // 一个点能到达它自己 (用于|=to[y]的传播)
        for (int y : e[x]) {
            // to[x][y] = 1; // x 能直接到达 y
            to[x] |= to[y]; // x 能到达所有 y 能到达的点
        }
    }

    // 计算 cm 集合：能到达每个点的点集
    // 按照拓扑序的正序处理
    for (int i = 1; i <= cnt; ++i) {
        int x = tupo[i];
        cm[x][x] = 1; // 一个点能被它自己到达 (用于|=cm[y]的传播)
        for (int y : back[x]) { // back[x] 存的是能直接到达 x 的点 (y -> x)
            // cm[x][y] = 1; // y 能直接到达 x
            cm[x] |= cm[y]; // 所有能到达 y 的点，也能到达 x
        }
    }

    int ans = 0;
    for (int i = 1; i <= m; ++i) {
        // 对于原始边 (u[i], v[i])
        // 我们需要检查是否存在一条 u[i] -> k -> v[i] 的路径，其中 k != u[i] 且 k != v[i]
        // to[u[i]] 包含 u[i] 和所有 u[i] 能到达的点
        // cm[v[i]] 包含 v[i] 和所有能到达 v[i] 的点

        // 临时拷贝，排除 u[i] 和 v[i] 本身
        bitset<N> temp_to = to[u[i]];
        temp_to[u[i]] = 0; // u[i] 到达的点不包括u[i]自身
        
        bitset<N> temp_cm = cm[v[i]];
        temp_cm[v[i]] = 0; // 能到达v[i]的点不包括v[i]自身

        // 如果 (u[i]能到达的点集，不含u[i]) 与 (能到达v[i]的点集，不含v[i]) 有交集
        // 这意味着存在一个中间点 k
        if ((temp_to & temp_cm).any()) { // .any() 检查bitset是否至少有一个1
            ans++;
        }
    }
    cout << ans << endl;
    return 0;
}